home *** CD-ROM | disk | FTP | other *** search
- /************************************************************
-
- DBGALLOC.C
-
- Written by Marc Stern, based on an initial idea
- from Piet Honkoop.
-
-
- Modifications in malloc(), realloc & free()
- to perform intensive consistency check.
-
- To use this object, just link it with your application
- BEFORE standard run-time lib.
-
- Warning: - This can only be used in large & huge models.
- - You cannot use _fmalloc()/farmalloc(),
- _frealloc()/farrealloc()
- _ffree()/farfree()
- _fcalloc()/farcalloc()
- fstrdup()
- - You will get three warnings from the linker
- (duplicate symbol). This is normal.
-
-
- Modifications to normal functions:
- ---------------------------------
-
- Allocated blocks are leaded and trailed by a block
- of the form:
-
- <total size of the block> (2 bytes)
- <number of the block> (2 bytes)
- '\xA6' ... '\xA6' (8 bytes)
- <number of the block> (2 bytes)
- <total size of the block> (2 bytes)
-
- Redundoncy is needed because the size could
- be overwritten if error.
-
-
- All allocated blocks are stored in a structure.
- This allow you to check from your application
- all the previously allocated blocks.
- You just have to call the function
-
- void allocCheck( void );
-
- A good place to call allocCheck() would be in an idle loop
- in your application (if any).
- ex. in TurboVision: the TApplication::idle() function.
-
- ************************************************************/
-
-
-
- #if defined( NDEBUG )
- #undef NDEBUG
- #endif
-
- #include <assert.h>
- #include <malloc.h>
- #include <stdlib.h>
- #include <string.h>
- #include <stdio.h>
- #include <conio.h>
- #include <dos.h>
- #include <io.h>
- #include <ctype.h>
-
- #if defined(_MSC_VER)
- # define heapOK() ( _heapchk() == _HEAPOK )
- #else
- # define heapOK() ( heapcheck() >= 0 )
- #endif
-
- #define uchar unsigned char
- #define ulong unsigned long
-
- #define pad_size 8
-
- /*
- This structure represents the block that will enclose
- all allocated ones.
- */
- typedef struct { size_t size1;
- size_t nb1;
- uchar padding[pad_size];
- size_t size2;
- size_t nb2;
- }
- Header;
-
- const uchar BLK_DATA = 0xA6;
-
-
- /* This allows to check up to 3000 previously allocated blocks */
- #define MAXSIZE 3000
-
-
- /*
- This structure represents the table that will contain
- all allocated blocks.
- */
- static struct
- {
- uchar *contents[MAXSIZE]; /* pointers to all allocated blocks */
- size_t total; /* number of currently stored pointers */
- int inFree; /* flag to see if we are checking the block to be freeed */
- int inError; /* flag to see if we are in msg_print function */
- /* because Microsoft uses malloc() inside fputs(),... */
- ulong totalAlloc; /* current total allocated bytes without the headers */
- ulong maxAlloc; /* maximum allocated bytes without the headers */
- } blocks;
-
-
- #define BADHEAP -1
- #define NOTFREEED -2
- #define MAXALLOC -3
-
- /*
- Display an error message giving the number of the block (if valid),
- its position (leading/trailing) and its contents.
- If no header given, assumes an error of trying to free a NULL
- pointer (which is actually a warning and not an error .
-
- The user has the choice to ignore (continue), to exit
- the program or to abort it (that means to stop it without
- closing open files, releasing extended/expanded memory,...
- */
-
- static void msg_print( long blk_nb, int delimiter, Header *header )
- {
- FILE *input = NULL;
- long inputTell;
- int i;
-
- blocks.inError = 1;
-
- switch( delimiter )
- {
- case MAXALLOC:
- fprintf( stderr, "\n Maximum allocated bytes (without headers): %lu\n", blocks.maxAlloc );
- break;
-
- case NOTFREEED:
- fprintf( stderr, "\n Warning: %d blocks not freeed.\n", blocks.total );
- break;
-
- case BADHEAP:
- fputs( "\n Error: Heap corrupted.\n", stderr );
- break;
-
- default:
- if ( ! header )
- fputs( "\n Error: trying to free a NULL pointer.\n", stderr );
- else
- {
- char fmt[255] = "\n"
- " Fatal error in function %s() in previously allocated block.\n"
- " Error in %sing header";
- if ( blk_nb >= 0 ) strcat( fmt, " - block n. %ld." );
- fprintf( stderr, fmt, blocks.inFree?"free":"allocCheck", delimiter?"trail":"lead", blk_nb );
-
- fprintf( stderr, "\n"
- " Header: size1 = %u, size2 = %u\n"
- " number1 = %u, number2 = %u\n"
- " padding = [",
- header->size1, header->size1,
- header->nb1, header->nb2
- );
-
- for ( i = 0; i < pad_size; i++ )
- fprintf( stderr, " %X", header->padding[i] );
-
- fputs( " ]\n", stderr );
- }
- } /* switch() */
-
- {
- char *msg;
-
- switch( delimiter )
- {
- case NOTFREEED:
- msg = NULL;
- break;
-
- case MAXALLOC:
- msg = " Any key to continue... ";
- break;
-
- default:
- msg = "\n(A)bort, (E)xit, (I)gnore ? ";
- }
- if ( msg ) fprintf( stderr, msg );
- flushall();
- }
-
- if ( delimiter == NOTFREEED ) return;
-
- blocks.inError = 0;
-
- /* if standard input is redirected, redirect it to console */
- {
- union REGS regs;
- regs.h.ah = 0x44; /* IOCTL function */
- regs.h.al = 0x00; /* subfunction */
- regs.x.bx = fileno(stdin);
- intdos( ®s, ®s );
- /* 0x80 -> character device; 0x01 -> stdin */
- if ( !(regs.x.dx & 0x80) || !(regs.x.dx & 0x01) )
- {
- inputTell = ftell( stdin );
- input = fdopen( dup(fileno(stdin)), "rt" );
- freopen( "CON", "rt", stdin );
- }
- }
-
- switch( toupper(getche()) )
- {
- case 'A': if ( delimiter != MAXALLOC ) abort();
- case 'E': if ( delimiter != MAXALLOC ) exit(-1);
- case 0 : getch();
- }
-
- blocks.inError = 1;
- fputs( "\n", stderr );
-
- flushall();
- if ( input )
- {
- /* restore original redirected stdin */
- fclose( stdin );
- fdopen( dup(fileno(input)), "rt" );
- if ( inputTell >= 0 ) fseek( stdin, inputTell, SEEK_SET );
- fclose( input );
- }
-
- blocks.inError = 0;
- }
-
-
-
- /* Check integrity of a block */
- static long check_blk( uchar *blk )
- {
- Header *header1, *header2;
- int i;
-
- /* Check leading block */
- header1 = (Header *) blk;
- if ( header1->nb1 != header1->nb2 )
- {
- /* Do not know the exact number */
- msg_print( -1, 0, header1 );
- return -1;
- }
-
- if ( header1->size1 != header1->size2 )
- {
- msg_print( header1->nb1, 0, header1 );
- return -1;
- }
-
- for ( i = 0; i < pad_size; i++ )
- if ( header1->padding[i] != BLK_DATA )
- {
- msg_print( header1->nb1, 0, header1 );
- return -1;
- }
-
- header2 = (Header *) (blk + header1->size1 - sizeof(Header) );
-
- /* Check trailing block */
- if ( memcmp(header1, header2, sizeof(Header)) )
- {
- msg_print( header1->nb1, 1, header2 );
- return -1;
- }
-
- return (long) header1->nb1;
- }
-
-
- /* Loop on all allocated blocks */
- void allocCheck( void )
- {
- size_t blk_nb;
-
- if ( blocks.inError ) return;
-
- if ( ! heapOK() )
- {
- msg_print( BADHEAP, BADHEAP, NULL );
- return;
- }
-
- for ( blk_nb = 0; blk_nb < blocks.total; blk_nb++ )
- check_blk( blocks.contents[blk_nb] );
- }
-
-
- /* internal function to write headers inside a block */
- static void buildHeader( void *blk, size_t size, size_t blk_nb )
- {
- Header *header;
-
- /* Construct leading block */
- header = (Header *) blk;
- header->size1 = header->size2 = size;
- header->nb1 = header->nb2 = blk_nb;
- memset( header->padding, BLK_DATA, pad_size ) ;
-
- /* Construct trailing block */
- header = (Header *) ((uchar*)blk + size - sizeof(Header) );
- header->size1 = header->size2 = size;
- header->nb1 = header->nb2 = blk_nb;
- memset( header->padding, BLK_DATA, pad_size ) ;
- }
-
-
- void checkNotFreeed( void )
- {
- if ( blocks.total ) msg_print( NOTFREEED, NOTFREEED, NULL );
- msg_print( MAXALLOC, MAXALLOC, NULL );
- }
-
-
- /* Overwrite RTL function */
- void *malloc( size_t size )
- {
- uchar *blk;
- size_t blk_nb;
- static int done = 0;
-
- if ( ! done )
- {
- atexit( checkNotFreeed );
- done = 1;
- }
-
- allocCheck();
-
- /* add the place needed to store the two headers */
- size += 2 * sizeof(Header);
-
- /* call the RTL malloc() in its far version */
- blk = (uchar *) _fmalloc( size );
-
- if ( ! blk ) return NULL;
-
- /* Store block in the table */
- if ( blocks.total < MAXSIZE )
- {
- blk_nb = blocks.total++;
- blocks.contents[blk_nb] = blk;
- }
- else blk_nb = MAXSIZE + 1; /* no storage */
-
- /* Construct headers */
- buildHeader( blk, size, blk_nb );
-
- /* update maximum allocated bytes */
- blocks.totalAlloc += size - 2 * sizeof(Header);
- blocks.maxAlloc = max( blocks.maxAlloc, blocks.totalAlloc );
-
- /* return to the user the block after the header. */
- return blk + sizeof(Header);
- }
-
-
-
- /* Overwrite RTL function */
- void *realloc( void *block, size_t size )
- {
- uchar *blk;
- Header *header;
-
- allocCheck();
-
- /* add the place needed to store the two headers */
- size += 2 * sizeof(Header);
-
- /* real block has a header before the block the user knows about. */
- blk = (uchar *) block - sizeof(Header);
-
- /* call the RTL realloc() in its far version */
- blk = (uchar *) _frealloc( blk, size );
-
- if ( ! blk ) return NULL;
-
- /* update maximum allocated bytes */
- blocks.totalAlloc += (ulong)size - (ulong)((Header*)blk)->size1;
- blocks.maxAlloc = max( blocks.maxAlloc, blocks.totalAlloc );
-
- /* Construct headers */
- header = (Header *)blk;
- buildHeader( blk, size, header->nb1 );
-
- /* Store block in the table (at the same place as before */
- if ( header->nb1 < MAXSIZE )
- blocks.contents[header->nb1] = blk;
-
- /* return to the user the block after the header. */
- return blk + sizeof(Header);
- }
-
-
-
- /* Overwrite RTL function */
- void free( void *block )
- {
- long blk_nb;
- Header *header;
-
- /* real block has a header before the block the user knows about. */
- uchar *blk = (uchar *)block - sizeof(Header);
-
- allocCheck();
-
- if ( ! block )
- {
- /*
- the next line may be suppressed to avoid
- messages about freeing NULL pointers
- */
- msg_print( 0, 0, NULL );
- return;
- }
-
- blocks.inFree = 1; /* flag to see if we are checking the block to be freeed */
-
- /* block to be freeed may not be in table (if invalid or table is full) */
- blk_nb = check_blk( blk );
-
- blocks.inFree = 0;
- if ( blk_nb < 0 ) return; /* error but user chose to ignore it (but we don't free) */
-
- /* update maximum allocated bytes */
- blocks.totalAlloc -= ((Header*)blk)->size1 - 2 * sizeof(Header);
-
- /* overwrite block header (if we try to free it again) */
- header = (Header *) blk;
- memset( blk + header->size1 - sizeof(Header), '\xFF', sizeof(Header) );
- memset( blk, '\xFF', sizeof(Header) );
-
- /*
- Remove block from the table and replace it with the last one
- (to not keep holes in the table).
- */
- if ( blk_nb < MAXSIZE )
- {
- blocks.total--;
- if ( blk_nb != (long)blocks.total )
- {
- blocks.contents[(size_t)blk_nb] = blocks.contents[blocks.total];
- header = (Header *) blocks.contents[(size_t)blk_nb];
- header->nb1 = header->nb2 = (size_t)blk_nb;
- header = (Header *) (blocks.contents[(size_t)blk_nb] + header->size1 - sizeof(Header) );
- header->nb1 = header->nb2 = (size_t)blk_nb;
- }
- blocks.contents[blocks.total] = 0;
- }
-
- /* call the RTL free() in its far version */
- _ffree( blk );
- }
-